#! /usr/bin/env python
# -*- coding: UTF-8 -*-

# This file is part of emule local client
# 
# The eMule Local Client is free software, you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# eMule Local Client is distributed in the hope that it will be useful
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with eMule Local Client; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

import gtk
import gobject
import Core

from constant import HISTORY_FILE, APP_DIR
from cPickle import UnpicklingError

(
        COLUMN_FILE_NAME,
        COLUMN_SIZE,
        COLUMN_HASH,
        COLUMN_SERVER,
        COLUMN_SESSION_ID,
        COLUMN_DATE,
        COLUMN_ORIG_URL
) = range(7)

COLUMNS = [
       # column name, column index, word wrap
       ('File Name', COLUMN_FILE_NAME, True),
       ('Size', COLUMN_SIZE, False),
       ('Hash', COLUMN_HASH, False),
       ('Server', COLUMN_SERVER, False),
       ('Session Id', COLUMN_SESSION_ID, False),
       ('Data', COLUMN_DATE, False),
       ('Original ED2K Link', COLUMN_ORIG_URL, False)
]

TREEVIEW_WRAP_WIDTH = 200

class HistoryWindow(gtk.Window):

    def __init__(self, parent, history_file=HISTORY_FILE, server=None, session_id=None):
        self.parent_window = parent
        self.history_file = history_file
        self.server = server
        self.session_id = session_id
        self.download_history = Core.load_download_info(self.history_file)

        gtk.Window.__init__(self)
        #self.set_position(gtk.WIN_POS_MOUSE)
        self.set_title('Historical download')
        self.set_default_size(800, 240)

        vbox = gtk.VBox()

        menubar = gtk.MenuBar()
        vbox.pack_start(menubar, expand=False)

        menu = gtk.Menu()
        menuitem = gtk.MenuItem('_File')

        open_menuitem = gtk.ImageMenuItem(gtk.STOCK_OPEN)
        purge_menuitem = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
        refresh_menuitem = gtk.ImageMenuItem(gtk.STOCK_REFRESH)
        exit_menuitem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
        open_menuitem.connect('activate', self.on_import_history)
        purge_menuitem.connect('activate', self.on_purge)
        refresh_menuitem.connect('activate', self.on_refresh)
        exit_menuitem.connect('activate', self.on_destroy)

        menu.add(open_menuitem)
        menu.add(purge_menuitem)
        menu.add(refresh_menuitem)
        menu.add(gtk.SeparatorMenuItem())
        menu.add(exit_menuitem)
        menuitem.set_submenu(menu)
        menubar.add(menuitem)

        self.add(vbox)
        sw = gtk.ScrolledWindow()
        sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)

        vbox.pack_start(sw)

        model = self.__create_model()
        self.treeview = gtk.TreeView(model)
        self.treeview.set_rules_hint(True)
        self.treeview.set_search_column(COLUMN_DATE)
        self.treeview.set_enable_search(True)
        self.treeview.connect('button_press_event', self.on_treeview_button_pressed)
        sw.add(self.treeview)
        self.__add_columns(self.treeview)
        self.connect('destroy', self.on_destroy)
        self.show_all()

    def on_treeview_button_pressed(self, treeview, event):
        if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3 and treeview.get_path_at_pos(int(event.x), int(event.y)) is not None:
            # show context menu
            menu = gtk.Menu()
            menuitem = gtk.ImageMenuItem(gtk.STOCK_REDO)
            menuitem.connect('activate', self.on_replay_download)
            if self.server is None or self.session_id is None:
                menuitem.set_sensitive(False)
            menuitem.show()
            menu.append(menuitem)
            menuitem = gtk.ImageMenuItem(gtk.STOCK_DELETE)
            menuitem.connect('activate', self.on_delete_record)
            menu.append(menuitem)
            menuitem.show()
            menu.popup(None, None, None, event.button, event.get_time())

    def on_delete_record(self, widget):
        (ls, iter) = self.treeview.get_selection().get_selected()
        if iter is not None:
            download_info = [str(ls.get_value(iter, i)) for i in range(len(COLUMNS))]
            self.download_history.remove(download_info)
            Core.dump_download_info(self.download_history, self.history_file)
            self.__refresh_treeview_model()

    def on_replay_download(self, widget):
        (ls, iter) = self.treeview.get_selection().get_selected()
        if iter is not None:
            download_info = [ls.get_value(iter, i) for i in range(len(COLUMNS))]
            Core.download(self.server, self.session_id, download_info[-1])
            # TODO: show a successful message dialog
            self.download_history.append(download_info)
            self.__refresh_treeview_model()

    def on_import_history(self, widget):
        chooser = gtk.FileChooserDialog(title='Select historical log', 
                                        action=gtk.FILE_CHOOSER_ACTION_OPEN,
                                        buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
        chooser.set_current_folder(APP_DIR)
        response = chooser.run()
        if response == gtk.RESPONSE_OK:
            try:
                self.download_history = self.get_download_history_from_dump_file(chooser.get_filename())
                self.__refresh_treeview_model()
            # when import an invalid historical file
            except UnpicklingError: 
                error = gtk.MessageDialog(self, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, 'Invalid historical records')
                error.run()
                error.destroy()

        chooser.destroy()

    def on_purge(self, widget):
        confirm_dialog = gtk.MessageDialog(parent=self, 
                                           flags=gtk.DIALOG_MODAL, 
                                           type=gtk.MESSAGE_WARNING, 
                                           buttons=gtk.BUTTONS_YES_NO,
                                           message_format='Confirm to purge historical download record?')
        response = confirm_dialog.run()

        if response == gtk.RESPONSE_YES:
            self.download_history = []
            # update user interface
            self.__refresh_treeview_model()
            # let the change apply permanently
            Core.dump_download_info(self.download_history, self.history_file)

        confirm_dialog.destroy()

    def __refresh_treeview_model(self):
        self.treeview.set_model(self.__create_model())

    def on_refresh(self, widget):
        self.download_history = self.get_download_history_from_dump_file(self.history_file)
        self.__refresh_treeview_model()

    def show(self):
        gtk.Window.show(self)

    def on_destroy(self, *args):
        gtk.main_quit() if self.parent_window == None else self.hide()

    def set_download_history(self, download_history):
        self.download_history = download_history

    def get_download_history(self):
        return self.download_history

    def get_download_history_from_dump_file(self, history_file=HISTORY_FILE):
        return Core.load_download_info(history_file)

    def __create_model(self):
        model = gtk.ListStore(
                        gobject.TYPE_STRING,
                        gobject.TYPE_UINT,
                        gobject.TYPE_STRING,
                        gobject.TYPE_STRING,
                        gobject.TYPE_STRING,
                        gobject.TYPE_STRING,
                        gobject.TYPE_STRING)

        for d in self.download_history:
            iter = model.append()
            model.set(iter, 
                COLUMN_FILE_NAME, d[COLUMN_FILE_NAME], 
                COLUMN_SIZE, int(d[COLUMN_SIZE]),
                COLUMN_HASH, d[COLUMN_HASH],
                COLUMN_SERVER, d[COLUMN_SERVER],
                COLUMN_SESSION_ID, d[COLUMN_SESSION_ID],
                COLUMN_DATE, d[COLUMN_DATE],
                COLUMN_ORIG_URL, d[COLUMN_ORIG_URL])

        return model

    def __add_columns(self, treeview):
        model = treeview.get_model()

        from pango import WRAP_CHAR
        '''
        The following call back method to let the tree view support wrap functionality
        is obtained from the unified resources identify
        http://code.google.com/p/pychess/source/browse/trunk/lib/pychess/System/uistuff.py?r=1025#62
        '''
        def callback(treeview, allocation, column, cell):
            other_columns = (c for c in treeview.get_columns() if c != column)
            new_width = allocation.width - sum(c.get_width() for c in other_columns)
            if cell.props.wrap_width == new_width or new_width <= 0:
                return
            cell.props.wrap_width = new_width
            store = treeview.get_model()
            iter = store.get_iter_first()
            while iter and store.iter_is_valid(iter):
                store.row_changed(store.get_path(iter), iter)
                iter = store.iter_next(iter)
            treeview.set_size_request(0, -1)
 
        for c, i, b in COLUMNS:
            cell = gtk.CellRendererText()
            # the condition b displayed twice make me sick:(
            if b:
                cell.props.wrap_mode = WRAP_CHAR
                cell.props.wrap_width = TREEVIEW_WRAP_WIDTH

            column = gtk.TreeViewColumn(c, cell, text=i)
            column.set_sort_column_id(i)
            column.set_resizable(True)
            treeview.append_column(column)

            if b:
                treeview.connect_after('size-allocate', callback, column, cell) 

def main():
    HistoryWindow(None, history_file=HISTORY_FILE)
    gtk.main()

if __name__ == '__main__':
    main()
